﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Text;
using System.Globalization;
using AutoMapper;
using GameUpWS.DTOs;
using System.Collections;
using System.IO;
using System.Drawing;
using DataLayer;
using DataLayer.Model;
using DataLayer.Model.enums;

namespace GameUpWS
{
    /// <summary>
    /// Summary description for Service1
    /// </summary>
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
    // [System.Web.Script.Services.ScriptService]
    public class GameUpWS : System.Web.Services.WebService
    {
        #region Types
        public enum GoalsType
        {
            Walking = 0,
            Climbing = 1
        };

        public enum Genders
        {
            Male = 0,
            Female = 1
        };
        static List<AuthSession> _Sessions = new List<AuthSession>();

        #endregion


        public GameUpWS()
        {
            Mapper.Initialize(t => t.AddProfile(new ServiceProfile()));
        }

        #region Methods



        #region Authentication

        [WebMethod]
        public string AuthenticatePlayer(string idCode, string MACAddress)
        {
            UnitOfWork repository = UnitOfWork.CreateIntance();

            //Does this STB exist?
            SetTopBox stb = repository.SetTopBoxRepository.GetSetTopBoxByMAC(MACAddress);
            if (stb == null)
                throw new Exception("The specified MAC address doesn't belong to a registered Set Top Box");

            //List the players assigned to this STB
            var player = stb.PlayerSetTopBoxes.FirstOrDefault(p => p.IDCode == idCode);

            if (player == null)
                throw new Exception("Specified user doesn't exist");

            //Check if this player has an active session, if so, terminate it
            var activesession = _Sessions.FirstOrDefault(x => x.IDPlayer == player.Id);
            if (activesession != null)
                _Sessions.Remove(activesession);

            AuthSession s = new AuthSession()
            {
                ID = Guid.NewGuid(),
                IDPlayer = player.Id,
                MACAddress = MACAddress
            };

            _Sessions.Add(s);
            return s.ToString();
        }

        AuthSession ValidateSession(string sessionId)
        {
            var session = _Sessions.FirstOrDefault(x => x.ToString() == sessionId);
            if (session == null)
                throw new Exception("Invalid session ID");

            //TODO, check if the session belongs to the same machine

            return session;
        }

        #endregion

        #region Testing

        [WebMethod]
        public string AuthenticatePlayerDebug(string idCode)
        {
            UnitOfWork repository = UnitOfWork.CreateIntance();

            //List the users assigned to this STB
            var player = repository.PlayerRepository.GetAll().FirstOrDefault(x => x.IDCode == idCode);

            if (player == null)
                throw new Exception("Specified user doesn't exist");

            //Check if this player has an active session, if so, terminate it
            var activesession = _Sessions.FirstOrDefault(x => x.IDPlayer == player.Id);
            if (activesession != null)
                _Sessions.Remove(activesession);

            AuthSession s = new AuthSession();
            s.IDPlayer = player.Id;
            s.ID = Guid.NewGuid();

            _Sessions.Add(s);
            return s.ToString();
        }

        [WebMethod]
        public List<GroupInfoDTO> GetGroupsInfo()
        {
            UnitOfWork repository = UnitOfWork.CreateIntance();
            var groups = repository.PlayerGroupRepository.GetAll();

            return Mapper.Map<List<GroupInfoDTO>>(groups);
        }

        #endregion

        #region UsersData
        [WebMethod]
        public PlayerDTO GetPlayerInfo(string sessionId)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            Player player = repository.PlayerRepository.GetById(session.IDPlayer);
            var playerDTO = Mapper.Map<PlayerDTO>(player);

            return playerDTO;
        }

        [WebMethod]
        public AdministratorDTO GetAdministratorInfo(string sessionId)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            Administrator admin = repository.PlayerRepository.GetById(session.IDPlayer).Administrator;
            return Mapper.Map<AdministratorDTO>(admin);
        }

        [WebMethod]
        public ProfessionalDTO GetProfessionalInfo(string sessionId)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            Professional professional = repository.PlayerRepository.GetById(session.IDPlayer).Professional;

            return Mapper.Map<ProfessionalDTO>(professional);
        }
        #endregion

        #region Messages
        [WebMethod]
        public List<PrivateMessageDTO> RetrieveUserMessages(string sessionId)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            var messages = repository.MessageRepository.GetMessagesReceivedByPlayerId(session.IDPlayer);
            return Mapper.Map<IEnumerable<Message>, List<PrivateMessageDTO>>(messages);
        }

        [WebMethod]
        public bool MarkMessageAsRead(string sessionId, int idMessage)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            Message message = repository.MessageRepository.GetMessagesReceivedByPlayerId(session.IDPlayer)
                                            .FirstOrDefault(m => m.Id == idMessage);

            if (message == null)
                throw new Exception("No message was found that matches the id");

            if (!message.IsRead)
            {
                repository.MessageRepository.MarkAsRead(idMessage);
                repository.Commit();
            }

            return true; //retrocompatibility: If there is a problem return exception, otherwise it's OK
        }

        [WebMethod]
        public bool SendMessage(string sessionId, int idReceiver, string subject, string content)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            Player senderPlayer = repository.PlayerRepository.GetById(session.IDPlayer); //Only player can send messages?
            Player receiverPlayer = repository.PlayerRepository.GetById(idReceiver);

            if (receiverPlayer == null)
                throw new ArgumentException("idReceiver not exist");

            Message NewMessage = new Message()
            {
                IdSender = senderPlayer,
                IdReceiver = receiverPlayer,
                DateTime = DateTime.Now,
                Subject = subject,
                Content = content
            };

            repository.MessageRepository.Insert(NewMessage);

            repository.Commit();

            return true; //retrocompatibility 
        }
        #endregion

        #region Exercises

        [WebMethod]
        public List<IndoorExerciseDTO> GetIndoorExercises(string sessionId)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            var exercises = repository.IndoorExercisePersonalRepository.GetActivateIndoorExerciseByPlayer(session.IDPlayer);

            return Mapper.Map<List<IndoorExerciseDTO>>(exercises);
        }

        [WebMethod]
        public bool SendIndoorExerciseResult(string sessionId, int exerciseId, int scorePercentage)
        {
            var session = ValidateSession(sessionId);

            if (scorePercentage < 0 || scorePercentage > 100)
                throw new Exception("Score percentage must be between 0 and 100");

            UnitOfWork repository = UnitOfWork.CreateIntance();

            IndoorExercisePersonal exercise = repository.IndoorExercisePersonalRepository.GetById(exerciseId);

            if (exercise == null)
                throw new Exception("No exercise was found that matches the id");

            if (exercise.IdPlayer.Id != session.IDPlayer)
                throw new Exception("You aren't the owner of this exercise");

            IndoorExerciseResult results = new IndoorExerciseResult()
            {
                IdExercise = exercise,
                DateTime = DateTime.Now,
                Score = scorePercentage
            };

            exercise.IndoorExerciseResults.Add(results);
            repository.Commit();

            return true; //retrocompatibility
        }

        [WebMethod]
        public OutdoorExerciseDTO GetOutdoorExercise(string sessionId, DateTime date)
        {
            throw new NotImplementedException("GetOutdoorExercise");
            //var session = ValidateSession(sessionId);

            //using (GameUPEntities dc = new GameUPEntities())
            //{
            //    var exercise = dc.OutdoorExercise.OrderByDescending(x => x.ActivationDate)
            //        .FirstOrDefault(x => x.Player.Id == session.IDPlayer && DateTime.Now >= x.ActivationDate);

            //    var result = Mapper.Map<OutdoorExercise, OutdoorExerciseDTO>(exercise);

            //    var player = dc.Player.FirstOrDefault(x => x.Id == session.IDPlayer);

            //    if (player.FitbitAccount != null)
            //    {

            //        DateTime startDate = date.Date;
            //        while (startDate.DayOfWeek != DayOfWeek.Monday)
            //            startDate = startDate.AddDays(-1.0);

            //        DateTime endDate = startDate.AddDays(7.0);

            //        if (exercise.ActivationDate > startDate)
            //            startDate = exercise.ActivationDate;

            //        var dataFilter = player.FitbitAccount.FitbitData.Where(y => y.Date >= startDate && y.Date < endDate);

            //        result.StartDate = startDate;

            //        result.EndDate = endDate;

            //        result.FloorsDone = (int)dataFilter.Sum(y => y.Floors);

            //        result.StepsDone = (int)dataFilter.Sum(y => y.Steps);

            //        result.DistanceDone = (int)dataFilter.Sum(y => y.Distance);
            //    }

            //    return result;
            //}
        }

        [WebMethod]
        public List<OutdoorRAWDataDTO> GetOurdoorRAWWeekly(string sessionId, DateTime selectedWeek)
        {
            DateTime startDate = selectedWeek.Date;
            while (startDate.DayOfWeek != DayOfWeek.Monday)
                startDate = startDate.AddDays(-1.0);

            DateTime endDate = startDate.AddDays(7.0);

            return GetOurdoorRAWInterval(sessionId, startDate, endDate);
        }

        [WebMethod]
        public List<OutdoorRAWDataDTO> GetOurdoorRAWInterval(string sessionId, DateTime startDate, DateTime endDate)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            var player = repository.PlayerRepository.GetById(session.IDPlayer);

            if (player.FitbitAccount == null) //TODO: this should be required in the database!!
                throw new InvalidDataException("Should be a fitbit account in the database for each Player :(");

            var dataFilter = player.FitbitAccount.FitbitDatas.Where(y => y.Date >= startDate.Date && y.Date < endDate.Date).OrderBy(x => x.Date);

            return Mapper.Map<IEnumerable<FitbitData>, List<OutdoorRAWDataDTO>>(dataFilter);
        }


        #endregion

        #region Social

        [WebMethod]
        public GroupDTO GetPlayerGroup(string sessionId)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            PlayerGroup playerGroup = repository.PlayerRepository.GetById(session.IDPlayer).PlayerGroup;

            return Mapper.Map<GroupDTO>(playerGroup);
        }

        [WebMethod]
        public List<OutdoorExerciseSocialDTO> GetGroupOutdoorExercises(string sessionId) //TODO: re-write please!! :D
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            PlayerGroup playerGroup = repository.PlayerRepository.GetById(session.IDPlayer).PlayerGroup;
            if (playerGroup == null)
                throw new Exception("User doesn't belong to any group");

            var validExercises = playerGroup.OutdoorExerciseSocials.Where(x => DateTime.Now >= x.OutdoorExercise.ActivationDate);

            var results = new List<OutdoorExerciseSocialDTO>();

            foreach (var exercise in validExercises)
            {
                OutdoorExerciseSocialDTO result = Mapper.Map<OutdoorExerciseSocialDTO>(exercise);

                result.StartDate = exercise.OutdoorExercise.ActivationDate;
                result.EndDate = DateTime.Today.Date;

                if (exercise.Type == OutdoorExerciseType.Climbing)
                {
                    result.FloorsDone = playerGroup.Players.Where(p => p.FitbitAccount != null).Sum(
                                  p => p.FitbitAccount.FitbitDatas.Where(
                                  y => y.Date >= exercise.OutdoorExercise.ActivationDate).Sum(y => y.Floors)).Value;
                }
                else
                {
                    result.StepsDone = playerGroup.Players.Where(p => p.FitbitAccount != null).Sum(
                                  p => p.FitbitAccount.FitbitDatas.Where(
                                  y => y.Date >= exercise.OutdoorExercise.ActivationDate).Sum(y => y.Steps)).Value;

                    result.DistanceDone = playerGroup.Players.Where(x => x.FitbitAccount != null).Sum(
                                  p => p.FitbitAccount.FitbitDatas.Where(
                                  y => y.Date >= exercise.OutdoorExercise.ActivationDate).Sum(y => y.Distance)).Value;
                }

                results.Add(result);                
            }
            return results;
        }

        [WebMethod]
        public byte[] GetGroupOutdoorImage(string sessionId, OutdoorExerciseType exerciseType)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            var playerGroup = repository.PlayerRepository.GetById(session.IDPlayer).PlayerGroup;
            if (playerGroup == null)
                throw new Exception("User doesn't belong to any group");

            OutdoorExerciseSocial validExercise = playerGroup.OutdoorExerciseSocials.FirstOrDefault(x => x.Type == exerciseType
                                            && DateTime.Now >= x.OutdoorExercise.ActivationDate);

            if (validExercise == null)
                return new byte[0]; //TODO: throw an exception please!!

            return ServiceProfile.imageToByteArray("outdoorSocial/", validExercise.ImageUrl);
        }

        #endregion

        #region Motivational
        [WebMethod]
        public List<CheerMessageDTO> GetCheeringForOutdoorExercise(string sessionId, int exerciseId)
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            var playerGroup = repository.PlayerRepository.GetById(session.IDPlayer).PlayerGroup;

            if (playerGroup == null)
                throw new ArgumentException("User doesn't belong to any group");

            OutdoorExerciseSocial exercise = playerGroup.OutdoorExerciseSocials.FirstOrDefault(x => x.Id == exerciseId);

            if (exercise == null)
                throw new Exception("No exercise was found that matches the id");

            var cheerMessages = exercise.OutdoorExerciseSocial_CheerMessage.Where(x => x.IdExercise == exerciseId);

            return Mapper.Map<List<CheerMessageDTO>>(cheerMessages);
        }

        [WebMethod]
        public bool SendCheerMessage(string sessionId, int cheerMessageId, int exerciseId)
        {
            throw new NotImplementedException("SendCheerMessage");
            //var session = ValidateSession(sessionId);

            //bool result = false;
            //using (GameUPEntities dc = new GameUPEntities())
            //{
            //    var message = dc.CheerMessage.FirstOrDefault(x => x.Id == cheerMessageId);
            //    if (message == null)
            //        throw new Exception("No cheer message was found that matches the id");

            //    var playerGroup = dc.Player.FirstOrDefault(x => x.Id == session.IDPlayer).PlayerGroup;
            //    if (playerGroup == null)
            //        throw new Exception("User doesn't belong to any group");

            //    var exercise = playerGroup.OutdoorExerciseSocial.FirstOrDefault(x => x.Id == exerciseId);
            //    if (exercise == null)
            //        throw new Exception("No exercise was found that matches the id");

            //    dc.OutdoorExerciseSocial_CheerMessage.AddObject(new OutdoorExerciseSocial_CheerMessage()
            //    {
            //        IdExercise = exerciseId,
            //        IdMessage = cheerMessageId,
            //        IdSender = session.IDPlayer,
            //        Datetime = DateTime.Now
            //    });

            //    dc.SaveChanges();
            //    result = true;
            //}

            //return result;
        }

        [WebMethod]
        public CheerMessageGroupDTO GetCheeringMessages(string sessionId, string language) //the method works but delegate the process to automapper :(
        {
            var session = ValidateSession(sessionId);

            UnitOfWork repository = UnitOfWork.CreateIntance();

            var cheeringGroup = repository.CheerMessageRepository.GetCheerMessageLanguagePack(language);

            if (cheeringGroup == null)
                throw new Exception("Language not supported");

            return Mapper.Map<CheerMessageGroupDTO>(cheeringGroup);
        }

        #endregion

        #endregion
    }
}
